﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System.Collections.Specialized;
using System.ComponentModel;
using System.Collections;
using System.Windows.Input;
using System.Windows.Data;
using Fayshine.VA.Control;

namespace System.Windows.Controls
{
    /// <summary>
    /// Represents a <see cref="T:System.Windows.Controls.DataGrid" /> column that hosts 
    /// <see cref="T:System.Windows.Controls.CheckBox" /> controls in its cells.
    /// </summary>
    /// <QualityBand>Mature</QualityBand>
    [StyleTypedProperty(Property = "ElementStyle", StyleTargetType = typeof(ComboBox))]
    [StyleTypedProperty(Property = "EditingElementStyle", StyleTargetType = typeof(ComboBox))]
    public class DataGridComboBoxColumn : DataGridBoundColumn
    {
        #region Constant

        #endregion

        #region Data

        private ComboBox _currentComboBox;
        //private DataGrid _ownDataGrid;
        private BindingBase _selectedValueBinding;
        private BindingBase _selectedItemBinding;
        private bool _selectedValueBindingEnsured = true;
        private bool _selectedItemBindingEnsured = true;

        #endregion

        public DataGridComboBoxColumn()
        {
          
        }


        #region ComboBox Column Properties

        /// <summary>
        ///     The ComboBox will attach to this ItemsSource.
        /// </summary>
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for ItemsSource.
        /// </summary>
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(DataGridComboBoxColumn), new PropertyMetadata(ItemsSourcePropertyChangedCallBack));

        /// <summary>
        /// Callback Event for ItemsSource property changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void ItemsSourcePropertyChangedCallBack(DependencyObject sender, DependencyPropertyChangedEventArgs args)   
        {   
            if (sender != null)   
            {
                //DataGridComboBoxColumn dataGridColumn = sender as DataGridComboBoxColumn;
                //dataGridColumn.RefreshCellContent(null, null);
                
            }   
        }  

        /// <summary>
        /// DisplayMemberPath is a simple way to define a default template
        /// that describes how to convert Items into UI elements by using
        /// the specified path.
        /// </summary>
        public string DisplayMemberPath
        {
            get { return (string)GetValue(DisplayMemberPathProperty); }
            set { SetValue(DisplayMemberPathProperty, value); }
        }

        /// <summary>
        ///     The DependencyProperty for the DisplayMemberPath property.
        /// </summary>
        public static readonly DependencyProperty DisplayMemberPathProperty =
               DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(DataGridComboBoxColumn), null);    

        /// <summary>
        ///  The path used to retrieve the SelectedValue from the SelectedItem
        /// </summary>
        public string SelectedValuePath
        {
            get { return (string)GetValue(SelectedValuePathProperty); }
            set { SetValue(SelectedValuePathProperty, value); }
        }

        /// <summary>
        ///     SelectedValuePath DependencyProperty
        /// </summary>
        public static readonly DependencyProperty SelectedValuePathProperty =
                  DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(DataGridComboBoxColumn), null);


        public double ComboxWidth
        {
            get { return (double)GetValue(ComboxWidthProperty); }
            set { SetValue(ComboxWidthProperty, value); }
        }

        public static readonly DependencyProperty ComboxWidthProperty =
                  DependencyProperty.Register("ComboxWidth", typeof(double), typeof(DataGridComboBoxColumn), new PropertyMetadata((double)100));

        /// <summary>
        /// Get or set combox SelectedValue binding
        /// </summary>
        public virtual BindingBase SelectedValueBinding
        {
            get 
            {
                if (!_selectedValueBindingEnsured)
                {
                    if (!IsReadOnly)
                    {
                        DataGridHelper.EnsureTwoWayIfNotOneWay(_selectedValueBinding);
                    }
                    _selectedValueBindingEnsured = true;
                }
                return _selectedValueBinding;
            }
            set
            {
                if (_selectedValueBinding != value)
                {
                    BindingBase oldBinding = _selectedValueBinding;
                    _selectedValueBinding = value;
                    _selectedValueBindingEnsured = false;
                    OnSelectedValueBindingChanged(oldBinding, _selectedValueBinding);
                }
            }
        }

        /// <summary>
        ///     The binding that will be applied to the SelectedItem property of the ComboBoxValue.
        /// </summary>
        /// <remarks>
        ///     This isn't a DP because if it were getting the value would evaluate the binding.
        /// </remarks>
        public virtual BindingBase SelectedItemBinding
        {
            get
            {
                if (!_selectedItemBindingEnsured)
                {
                    if (!IsReadOnly)
                    {
                        DataGridHelper.EnsureTwoWayIfNotOneWay(_selectedItemBinding);
                    }

                    _selectedItemBindingEnsured = true;
                }

                return _selectedItemBinding;
            }

            set
            {
                if (_selectedItemBinding != value)
                {
                    BindingBase oldBinding = _selectedItemBinding;
                    _selectedItemBinding = value;
                    _selectedItemBindingEnsured = false;
                    OnSelectedItemBindingChanged(oldBinding, _selectedItemBinding);
                }
            }
        }

        /// <summary>
        ///     Called when SelectedValueBinding changes.
        /// </summary>
        /// <param name="oldBinding">The old binding.</param>
        /// <param name="newBinding">The new binding.</param>
        protected virtual void OnSelectedValueBindingChanged(BindingBase oldBinding, BindingBase newBinding)
        {
            NotifyPropertyChanged("SelectedValueBinding");
        }

        /// <summary>
        ///     Called when SelectedItemBinding changes.
        /// </summary>
        /// <param name="oldBinding">The old binding.</param>
        /// <param name="newBinding">The new binding.</param>
        protected virtual void OnSelectedItemBindingChanged(BindingBase oldBinding, BindingBase newBinding)
        {
            NotifyPropertyChanged("SelectedItemBinding");
        }

        #endregion

        #region Protected Methods

        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            bool isEnabled = false;
            ComboBox checkBoxElement = new ComboBox();
            
            checkBoxElement.IsEnabled = isEnabled;
           // checkBoxElement.IsHitTestVisible = false;
            ConfigureComboBox(checkBoxElement);
            _currentComboBox = checkBoxElement;
            return checkBoxElement;
        }

        protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
        {
            ComboBox comboBox = new ComboBox();

            //comboBox.Margin = new Thickness(0);

            ConfigureComboBox(comboBox);

            _currentComboBox = comboBox;

            return comboBox;
        }

        /// <summary>
        /// Causes the column cell being edited to revert to the specified value.
        /// </summary>
        /// <param name="editingElement">
        /// The element that the column displays for a cell in editing mode.
        /// </param>
        /// <param name="uneditedValue">
        /// The previous, unedited value in the cell being edited.
        /// </param>
        protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
        {
            ComboBox comboBox = editingElement as ComboBox;
            if (comboBox != null)
            {
                DataGridHelper.UpdateTarget(comboBox, ComboBox.SelectedValueProperty);
                DataGridHelper.UpdateTarget(comboBox, ComboBox.SelectedItemProperty);    
            }
        }

        protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
        {
            ComboBox comboBox = editingElement as ComboBox;
            if (comboBox != null)
            {
                comboBox.Focus();
                object originalValue = GetComboBoxSelectionValue(comboBox);

                if (IsComboBoxOpeningInputEvent(editingEventArgs))
                {
                    comboBox.IsDropDownOpen = true;
                }

                return originalValue;
            }

            return null;
        }

        protected override void RefreshCellContent(FrameworkElement element, string propertyName)
        {
            ComboBox comboBox = element as ComboBox;
            
            switch (propertyName)
            {
                case "SelectedValue":
                    DataGridHelper.SyncColumnProperty(this, comboBox, ComboBox.SelectedValueProperty, SelectedValuePathProperty);
                    break;
                case "SelectedValuePath":
                    DataGridHelper.SyncColumnProperty(this, comboBox, ComboBox.SelectedValuePathProperty, SelectedValuePathProperty);
                    break;
                case "DisplayMemberPath":
                    DataGridHelper.SyncColumnProperty(this, comboBox, ComboBox.DisplayMemberPathProperty, DisplayMemberPathProperty);
                    break;
                case "ItemsSource":
                    DataGridHelper.SyncColumnProperty(this, comboBox, ComboBox.ItemsSourceProperty, ItemsSourceProperty);
                    break;
                default:
                    base.RefreshCellContent(element, propertyName);
                    break;
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Config combox property and set its binding
        /// </summary>
        /// <param name="comboBox">The specified combox</param>
        private void ConfigureComboBox(ComboBox comboBox)
        {
            comboBox.HorizontalAlignment = HorizontalAlignment.Center;
            comboBox.VerticalAlignment = VerticalAlignment.Center;
            comboBox.Width = this.ComboxWidth;

            ApplyBinding(SelectedValueBinding, comboBox, ComboBox.SelectedValueProperty);
            ApplyBinding(SelectedItemBinding, comboBox, ComboBox.SelectedItemProperty);

            if (this.ItemsSource != null)
            {
                comboBox.SetValue(ComboBox.ItemsSourceProperty, this.ItemsSource);
            }

            if (this.SelectedValuePath != null)
            {
                comboBox.SetValue(ComboBox.SelectedValuePathProperty, this.SelectedValuePath);
            }
            if (this.DisplayMemberPath != null)
            {
                comboBox.SetValue(ComboBox.DisplayMemberPathProperty, this.DisplayMemberPath);
            }
        }

        private static bool IsComboBoxOpeningInputEvent(RoutedEventArgs e)
        {
            KeyEventArgs keyArgs = e as KeyEventArgs;
            if (keyArgs != null)
            {

                // We want to handle the ALT key. Get the real key if it is Key.System.
                Key key = keyArgs.Key;


                // F4 alone or ALT+Up or ALT+Down will open the drop-down
                return (key == Key.F4 ||
                       (((key == Key.Up) || (key == Key.Down))));
            }

            return false;
        }

        private static void ApplyBinding(BindingBase binding, DependencyObject target, DependencyProperty property)
        {
            if (binding != null)
            {
                BindingOperations.SetBinding(target, property, binding);
            }
        }

        /// <summary>
        /// Helper method which returns selection value from
        /// combobox based on which Binding's were set.
        /// </summary>
        /// <param name="comboBox"></param>
        /// <returns></returns>
        private object GetComboBoxSelectionValue(ComboBox comboBox)
        {
            if (SelectedItemBinding != null)
            {
                return comboBox.SelectedItem;
            }
            else if (SelectedValueBinding != null)
            {
                return comboBox.SelectedValue;
            }
            return null;
        }

        private void RefreshComboBoxContext()
        {
            if (this._currentComboBox != null)
            {
                this._currentComboBox.SetValue(ComboBox.SelectedValueProperty, this.SelectedValueBinding);
            }
        }

        #endregion

    }
}
